﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Xml.Serialization;
using MathematicallySafe.HeadCounter.Data.Common;
using MathematicallySafe.HeadCounter.Data.Interfaces;
using StructureMap;

namespace MathematicallySafe.HeadCounter.Data.Models
{
    [Serializable]
    public class Raid
    {
        private static IGuild _guild;
        private static IGuild Guild
        {
            get
            {
                if (_guild == null)
                {
                    _guild = ObjectFactory.GetInstance<IGuild>();
                }

                return _guild;
            }
        }

        public Raid()
        {
            Attendees = new List<RaidAttendance>();
            ExtraAttendees = new List<string>();
            Loot = new List<RaidLoot>();
            BossKills = new List<string>();
        }

        [XmlAttribute]
        public DateTime Date { get; set; }

        [XmlIgnore]
        public int WeekNumber
        {
            get
            {
                return Date.GetWeekNumber();
            }
        }

        [XmlAttribute]
        public string Zone { get; set; }

        [XmlAttribute]
        public RaidDifficulty Difficulty { get; set; }

        [XmlAttribute]
        public bool Optional { get; set; }

        public bool IsOptional { get { return Optional; } }

        public List<RaidAttendance> Attendees { get; set; }
        public List<string> ExtraAttendees { get; set; }
        public List<RaidLoot> Loot { get; set; }
        public List<string> BossKills { get; set; }

        public IEnumerable<Raider> Raiders
        {
            get
            {
                return from n in Attendees
                       orderby n.RaidingAs
                       select Guild.FindRaider(n.RaiderIdentity);
            }
        }

        public IEnumerable<String> RaiderNames
        {
            get
            {
                return from n in Attendees
                       orderby n.RaidingAs
                       select n.RaidingAs;
            }
        }

        public IEnumerable<String> RaiderIdentities
        {
            get
            {
                return from n in Attendees
                       orderby n.RaiderIdentity
                       select n.RaiderIdentity;
            }
        }

        public bool RaiderAttendedAnyChar(string raiderName)
        {
            if (RaiderIdentities.Contains(raiderName)) return true;
            return Raiders.FirstOrDefault(r => r.HasCharacter(raiderName)) != null;
        }

        public void AddRaider(string name, int minutes)
        {
            var raider = Guild.FindRaider(name);

            if (raider != null)
            {
                var attendee = new RaidAttendance()
                               {
                                   RaiderIdentity = raider.Identity,
                                   RaidingAs = name,
                                   Minutes = minutes
                               };

                Attendees.Add(attendee);
            }
            else
            {
                throw new ArgumentException("No raider named " + name + " exists");
            }
        }

        public void RemoveRaider(string name)
        {
            var options = ObjectFactory.GetInstance<IOptions>();
            var raider = Guild.FindRaider(name);

            if (raider != null)
            {
                var loot = Loot.Where(l => l.WinnerIdentity == raider.Identity).ToList();
                loot.ForEach(deadLoot => Loot.Remove(deadLoot));

                var attendee = Attendees.FirstOrDefault(a => a.RaiderIdentity == raider.Identity);
                if (attendee != null)
                {
                    Attendees.Remove(attendee);

                    if (options.AutoApplyAttendanceCreditToDeletedRaiders)
                    {
                        ExtraAttendees.Add(attendee.RaiderIdentity);    
                    }

                    OnAttendanceChanged();
                }
            }
            else
            {
                throw new ArgumentException("No raider named " + name + " exists");
            }
        }

        public void AddLoot(int id, DateTime time, string name, string boss, string winner, LootOutcome outcome)
        {
            var loot = new RaidLoot()
                       {
                           ID = id,
                           ItemName = name,
                           Time = time,
                           Boss = boss,
                           Winner = winner,
                           Outcome = outcome
                       };

            Loot.Add(loot);

            if (!string.IsNullOrEmpty(winner))
            {
                var raider = Guild.FindRaider(winner);

                if (raider != null)
                {
                    raider.AddGear(id, true);
                }
            }
        }

        public void AddExtraAttendee(string identity)
        {
            if (!ExtraAttendees.Contains(identity))
            {
                ExtraAttendees.Add(identity);
                OnAttendanceChanged();
            }
        }

        public void RemoveExtraAttendee(string identity)
        {
            if (ExtraAttendees.Contains(identity))
            {
                ExtraAttendees.Remove(identity);
                OnAttendanceChanged();
            }
        }

        public bool IsExtraAttendee(string identity)
        {
            return ExtraAttendees.Contains(identity);
        }

        public void AddBoss(string boss)
        {
            BossKills.Add(boss);
        }

        [XmlIgnore]
        public Action<Raid> AttendanceChanged;

        protected void OnAttendanceChanged()
        {
            if (AttendanceChanged != null)
            {
                AttendanceChanged(this);
            }
        }

        #region Events

        //        public Action LootUpdated;
        //
        //        protected void OnLootUpdated()
        //        {
        //            if (LootUpdated == null)
        //            {
        //                LootUpdated();
        //            }
        //        }
        //
        //        private void SubcribeLootEvents()
        //        {
        //            Loot.ForEach(loot =>
        //            {
        //                loot.WinnerChanged += LootWinnerChanged;
        //                loot.OutcomeChanged += LootOutcomeChanged;
        //            });
        //        }
        //
        //        private void UnsubcribeLootEvents()
        //        {
        //            Loot.ForEach(loot =>
        //            {
        //                loot.WinnerChanged -= LootWinnerChanged;
        //                loot.OutcomeChanged -= LootOutcomeChanged;
        //            });
        //        }
        //
        //        void LootOutcomeChanged(object sender, OutcomeChangedEventArgs e)
        //        {
        //        }
        //
        //        void LootWinnerChanged(object sender, WinnerChangedEventArgs e)
        //        {
        //        }

        #endregion

    }
}